package httpproxy.client;

import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class HttpRequest
{
	private String id="";
	
	private String method = "GET";
	private String url = "";
	private String version = "";
	private String userAgent = "";
	private String accept = "";
	private String acceptCharset = "";
	private String acceptLanguage = "";
	private String acceptEncoding  = "";
	private String acceptRanges  = "";
	private String age = "";
	private String allow = "";
	private String cacheControl = "";
	private String cookie = "";
	private String host = "";
	private String path = "";
	private String referer = "";
	private byte[] contentData = null;
	private int _content_length = 0;
	private String contentLanguage;
	private String contentType  = "";
	private String contentEncoding  = "";
	private String proxy_connection  = "";
	private String connection  = "";
	private int port = 80;
	
	private String ifModifiedSince = "";
	private String authorization = "";
	private boolean pragmaNoCache = true;
	private String unrecognized = "";
	
	private Socket webServerSocket = null;
	private InputStream serverInputStream = null;
	private OutputStream  serverOutputStream = null;
	
	private ArrayList<String> lst = new ArrayList<String>();
	private boolean isChainPx = false;
	
	public HttpRequest(String _id)
	{
		id = _id;
	} 
	
	/*
	 * 	Trong truong hop co su dung chain proxy thi
	 * 	goi Constructor nay va truyen vao dia chi/ port cua chain
	 * 	proxy
	 * */
	public HttpRequest(String _id, InetAddress chainProxyAddress, int chainPort)
	{
		id = _id;
		isChainPx = true;
		try {
			webServerSocket = new Socket(chainProxyAddress, chainPort);
			serverOutputStream = webServerSocket.getOutputStream();
			serverInputStream = webServerSocket.getInputStream();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public String getMethod() 
	{
		return method;
	}
	
	public void setMethod(String _method) 
	{
		this.method = _method;
	}

	public String getUserAgent() 
	{
		return userAgent;
	}

	public void setUserAgent(String userAgent) {
		this.userAgent = userAgent;
	}

	public String getAccept() {
		return accept;
	}

	public void setAccept(String accept) {
		this.accept = accept;
	}

	public String getAcceptCharset() {
		return acceptCharset;
	}

	public void setAcceptCharset(String acceptCharset) {
		this.acceptCharset = acceptCharset;
	}

	public String getAcceptLanguage() {
		return acceptLanguage;
	}

	public void setAcceptLanguage(String acceptLanguage) {
		this.acceptLanguage = acceptLanguage;
	}

	public String getAcceptEncoding() {
		return acceptEncoding;
	}

	public void setAcceptEncoding(String acceptEncoding) {
		this.acceptEncoding = acceptEncoding;
	}

	public String getAcceptRanges() {
		return acceptRanges;
	}

	public void setAcceptRanges(String acceptRanges) {
		this.acceptRanges = acceptRanges;
	}

	public String getAge() {
		return age;
	}

	public void setAge(String age) {
		this.age = age;
	}

	public String getAllow() {
		return allow;
	}

	public void setAllow(String allow) {
		this.allow = allow;
	}

	public String getCacheControl() {
		return cacheControl;
	}

	public void setCacheControl(String cacheControl) {
		this.cacheControl = cacheControl;
	}

	public String getCookie() {
		return cookie;
	}

	public void setCookie(String cookie) {
		this.cookie = cookie;
	}

	public String getHost() {
		return host;
	}

	public void setHost(String _host) {
		this.host = _host;
	}

	public String getPath() {
		return path;
	}

	public void setPath(String _path) {
		this.path = _path;
	}

	public byte[] getContentData() {
		return contentData;
	}

	public void setContentData(byte[] _data) {
		this.contentData = _data;
	}

	public int getContentLength() 
	{
		return _content_length;
	}

	public void setContentLength(int _content_length) {
		this._content_length = _content_length;
	}

	public String getContentLanguage() {
		return contentLanguage;
	}

	public void setContentLanguage(String contentLanguage) {
		this.contentLanguage = contentLanguage;
	}

	public String getContentEncoding() {
		return contentEncoding;
	}

	public void setContentEncoding(String contentEncoding) {
		this.contentEncoding = contentEncoding;
	}

	public String getProxyConnection() {
		return proxy_connection;
	}

	public void setProxyConnection(String _proxy_connection) {
		this.proxy_connection = _proxy_connection;
	}

	public String getConnection() {
		return connection;
	}

	public void setConnection(String _connection) {
		this.connection = _connection;
	}

	public int getPort() {
		return port;
	}

	public void setPort(int _port) {
		this.port = _port;
	}

	public ArrayList<String> getLst() {
		return lst;
	}

	public void setLst(ArrayList<String> lst) {
		this.lst = lst;
	}
	
	public Socket getWebServerSocket() {
		return webServerSocket;
	}

	public void setWebServerSocket(Socket webServerSocket) {
		this.webServerSocket = webServerSocket;
	}

	public InputStream getServerInputStream() {
		return serverInputStream;
	}

	public void setServerInputStream(InputStream serverInputStream) {
		this.serverInputStream = serverInputStream;
	}

	public OutputStream getServerOutputStream() {
		return serverOutputStream;
	}

	public void setServerOutputStream(OutputStream serverOutputStream) {
		this.serverOutputStream = serverOutputStream;
	}

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	public String getVersion() {
		return version;
	}

	public void setVersion(String version) {
		this.version = version;
	}

	public String getReferer() {
		return referer;
	}

	public void setReferer(String referer) {
		this.referer = referer;
	}

	public String getContentType() {
		return contentType;
	}

	public void setContentType(String contentType) {
		this.contentType = contentType;
	}

	public String getIfModifiedSince() {
		return ifModifiedSince;
	}

	public void setIfModifiedSince(String ifModifiedSince) {
		this.ifModifiedSince = ifModifiedSince;
	}

	public String getAuthorization() {
		return authorization;
	}

	public void setAuthorization(String authorization) {
		this.authorization = authorization;
	}
	
	public boolean parse(InputStream ins)
	{
	       //String CR ="\r\n";
	       //String ENDR = "\r\n\r\n";
	       int len = 0;
	       byte[] temp, buff = new byte[1024];
	       
	       byte[] oneByte = new byte[1];
	       /*
	        * Read by lines
	        */
	       String[] lines = null;
	       String s="";
	       try {
	    	   
	    	   while(ins.read(oneByte) > -1)
	    	   {
	    		   if(len == buff.length)
	    		   {
	    			   temp = buff;
	    			   buff = new byte[buff.length + 512];
	    			   for(int i=0;i<len;i++)
	    				   buff[i] = temp[i];
	    			   temp = null;
	    		   }
	    		   
	    		   buff[len++] = oneByte[0];
	    		   if(len>=4)
	    			   s = new String(buff, len-4, 4);
	    		   
	    		   
	    		   if( len>=4 && s.contains("\r\n\r\n") )
	    		   {
	    			   String headers = new String(buff, 0, len);
	    			   lines = ((headers.split("\r\n\r\n"))[0]).split("\r\n");
	    			   break;
	    		   }
	    	   }
	       } catch (IOException e) {
	           return false;
	       }

	       if(lines != null)
	    	   for(String line:lines)
	    	   {
	    		   //System.out.println(line);
	    		   extractRequestHeader(line);
	    	   }
	       
	       //Neu khong co chain proxy thi gui request truc tiep den web server
	       try{
		       if(!this.isChainPx)
		       { 
		    	   webServerSocket = new Socket(InetAddress.getByName(host), port);	
		    	   serverOutputStream = webServerSocket.getOutputStream();
		    	   serverInputStream = webServerSocket.getInputStream();
		       }
	       }catch(IOException ex)
	       {
	    	   ex.printStackTrace();
	    	   return false;
	       }
	       
	       
	       return true;
	}
	
	public void serverNameAndPort(String surl)
	{
		Pattern portP = Pattern.compile(":[\\d]+");
		Matcher portM = portP.matcher(surl);
		String sPort="", hostLine;
		
		if(portM.find())
		{
			sPort = (surl.substring(portM.start(), portM.end())).replaceAll(":", "");
			if(sPort!=null && sPort.trim().length()>0)
				port = Integer.parseInt(sPort);
		}
		//Regular Expression
		//hostLine = (line.replaceFirst("Host", "")).replaceFirst(":", "").trim();
		hostLine = surl.replaceFirst(sPort, "").trim();
		
		int idx = hostLine.indexOf("//");
		hostLine = hostLine.substring(idx + (idx>=0?2:0));
		
		Pattern host_pattern = Pattern.compile("([a-zA-Z0-9]+[a-zA-Z0-9\\-]*[a-zA-Z0-9][\\.]*)+");
		Matcher m_host = host_pattern.matcher(hostLine);
		if(m_host.find())
		{
			host = hostLine.substring(m_host.start(), m_host.end());
		}
		
		//System.out.println(surl + "\n" + host + " - " + port);
	}

	
	public void extractRequestHeader(String line)
	{
		String hostLine, sPort="";
		
		if(line.trim().toUpperCase().startsWith("GET") || line.trim().toUpperCase().startsWith("POST"))
		{
			String s[] = line.trim().split("\\s");
			method = s[0];
		    url    = s[1];
		    version= s[2];
		}
			
		if(line.trim().startsWith("Host"))
		{
			Pattern portP = Pattern.compile(":[\\d]+");
			Matcher portM = portP.matcher(line);
			
			if(portM.find())
			{
				sPort = (line.substring(portM.start(), portM.end())).replaceAll(":", "");
				if(sPort!=null && sPort.trim().length()>0)
					port = Integer.parseInt(sPort);
			}
			//Regular Expression
			//hostLine = (line.replaceFirst("Host", "")).replaceFirst(":", "").trim();
			hostLine = (line.replaceFirst("Host:", "")).replaceFirst(sPort, "").trim();
			
			Pattern host_pattern = Pattern.compile("([a-zA-Z0-9]+[a-zA-Z0-9\\-]*[a-zA-Z0-9][\\.]*)+");
			Matcher m_host = host_pattern.matcher(hostLine);
			if(m_host.find())
			{
				host = hostLine.substring(m_host.start(), m_host.end());
			}
			
			//System.out.println(line + "\n" + host + " - " + port);
		}
			
		if(line.trim().startsWith("Connection") || line.trim().startsWith("Proxy-Connection"))
		{
			if(this.isChainPx)
			{
				//connection = (line.replace("Connection", "")).replace(":", "").trim();
				proxy_connection = (line.replace("Proxy-Connection", "")).replace(":", "").trim();
			}
			else
				connection = (line.replace("Connection", "")).replace(":", "").trim();
			
		}
			
		if(line.trim().startsWith("Content-Length") )
		{
			_content_length = Integer.parseInt((line.replace("Content-Length", "")).replace(":", "").trim());
		}
			
		if(line.trim().startsWith("User-Agent") )
		{
			userAgent = (line.replace("User-Agent", "")).replace(":", "").trim();
		}
		
		if(line.trim().startsWith("Cookie") )
		{
			cookie = (line.replace("Cookie", "")).replace(":", "").trim();
		}
		
		lst.add(line);
	}
	
	public String toString(boolean sendUnknowen) 
	{
		String CR = "\r\n";
		
	       String Request; 

	       if (0 == method.length())
	            method = "GET";

	       Request = method +" "+ url + " " + version + CR;

	       if (0 < userAgent.length())
	           Request +="User-Agent:" + userAgent + CR;

	       if (0 < referer.length())
	           Request+= "Referer:"+ referer  + CR;

	       if (pragmaNoCache)
	           Request+= "Pragma: no-cache" + CR;

	       if (0 < ifModifiedSince.length())
	           Request+= "If-Modified-Since: " + ifModifiedSince + CR;
	           
	       // ACCEPT TYPES //
	       if (0 < accept.length())
	           Request += "Accept: " + accept + CR;
	       else 
	           Request += "Accept: */"+"* \r\n";
	       
	       if (0 < acceptCharset.length())
	           Request += "Accept-Charset: " + acceptCharset + CR;
	       
	       if (0 < acceptEncoding.length())
	           Request += "Accept-Encoding: " + acceptEncoding + CR;
	    
	       if (0 < contentType.length())
	           Request += "Content-Type: " + contentType   + CR;

	       if (0 < _content_length)
	           Request += "Content-Length: " + _content_length + CR;
	       
	       if (0 < connection.length())
	           Request += "Connection: " + connection + CR;
	       
	       if (0 < proxy_connection.length())
	           Request += "Proxy-Connection: " + proxy_connection + CR;
	       
	       if (0 != authorization.length())
	           Request += "Authorization: " + authorization + CR;

	       if (sendUnknowen) {
	           if (0 != unrecognized.length())
	               Request += unrecognized;
	       }   

	       Request += CR;
	       
	       return Request;
	   }
	
	public void sendContentData() throws IOException
	{
		if(method.equals("POST"))
		{
			//System.out.println(String.format("[REQUEST SENT %s ] %s", id, new String(contentData, 0, _content_length, "UTF-8")));
			serverOutputStream.write(contentData);
			serverOutputStream.flush();
		}
	}
	
	public void readContentData(InputStream inStream)
	{
		try{
			contentData = new byte[_content_length];
			int count, byteread = 0;
			
			if(_content_length < 2048)
			{
				while( (count = inStream.read(contentData)) > -1 )
				{
					byteread += count;	
					if(byteread == _content_length)
						break;
				}
				
				return;
			}
			
			byte[] buff = new byte[1024];
			while( (count = inStream.read(buff)) > -1 )
			{
				for(int i=0; i<count; i++)
					contentData[byteread + i] = buff[i];
				byteread += count;	
				if(byteread == _content_length)
					break;
			}
		}catch(IOException ex)
		{
			ex.printStackTrace();
		}
	}
	
	public void sendRequestHeaderString() throws IOException
	{
		
			DataOutputStream dos = new DataOutputStream(serverOutputStream);
			String strHeader = toString(false);
			
			//System.out.println(strHeader);
			//System.out.println("**************END SEND REQUEST***************");
			//dos.writeBytes(strHeader);
			for(String line:lst)
			{
				//System.out.print(line + "\r\n");
				if(line.trim().startsWith("Connection") || line.trim().startsWith("Proxy-Connection"))
				{
					if(this.isChainPx)
					{
						dos.writeBytes("Proxy-Connection: " + proxy_connection + "\r\n");
					}
					else
						dos.writeBytes("Connection: " + connection + "\r\n");
				}
				else
					dos.writeBytes(line + "\r\n");
			}
			//System.out.print("\r\n");
			dos.writeBytes("\r\n");
			dos.flush();
	}
}